﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace BrandonHaynes.Data
	{
	public abstract class TypedDataRecordAdapterBase<T> : IDataRecord
		{
		#region Static Fields
		private static IDictionary<Type, MemberDictionary> MemberCache = new Dictionary<Type, MemberDictionary>();
		#endregion 

		#region Properties
		private MemberDictionary MemberMap { get; set; }

		private T adaptee;
		protected T Adaptee
			{
			get { return adaptee; }
			set
				{
				adaptee = value;

				if (!MemberCache.ContainsKey(adaptee.GetType()))
					MemberCache.Add(adaptee.GetType(), new MemberDictionary(Members));

				MemberMap = MemberCache[adaptee.GetType()];
				}
			}

		#endregion

		#region Constructors
		protected TypedDataRecordAdapterBase(T adaptee)
			{ Adaptee = adaptee; }

		internal TypedDataRecordAdapterBase()
			{ }
		#endregion

		#region Abstract Members
		protected abstract IList<string> Members { get; }
		public abstract object this[string name] { get; }
		#endregion

		#region Static Methods
		protected static object PrepareValue(object value)
			{ return value ?? DBNull.Value; }
		#endregion

		#region IDataRecord Members

		public object this[int i]
			{ get { return this[Members[i]]; } }

		public int GetOrdinal(string name)
			{ return MemberMap[name]; }

		public int FieldCount
			{ get { return MemberMap.Count; } }

		public string GetName(int i)
			{ return MemberMap[i]; }

		public bool GetBoolean(int i)
			{ return (bool)this[i]; }

		public byte GetByte(int i)
			{ return (byte)this[i]; }

		public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
			{
			var data = (byte[])this[i];

			if(length > data.Length)
				length = data.Length;

			Array.Copy(data, fieldOffset, buffer, bufferoffset, length);

			return length;
			}

		public char GetChar(int i)
			{ return (char)this[i]; }

		public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
			{
			var data = (char[])this[i];

			if (length > data.Length)
				length = data.Length;

			Array.Copy(data, fieldoffset, buffer, bufferoffset, length);

			return length;
			}

		public IDataReader GetData(int i)
			{
			return new ReflectedDataReaderAdapter(Enumerable.Repeat(this[i], 1));
			}

		public string GetDataTypeName(int i)
			{ return GetFieldType(i).Name; }

		public DateTime GetDateTime(int i)
			{ return (DateTime)this[i]; }

		public decimal GetDecimal(int i)
			{ return (decimal)this[i]; }

		public double GetDouble(int i)
			{ return (double)this[i]; }

		public Type GetFieldType(int i)
			{ return this[i].GetType(); }

		public float GetFloat(int i)
			{ return (float)this[i]; }

		public Guid GetGuid(int i)
			{ return (Guid)this[i]; }

		public short GetInt16(int i)
			{ return (short)this[i]; }

		public int GetInt32(int i)
			{ return (int)this[i]; }

		public long GetInt64(int i)
			{ return (long)this[i]; }

		public string GetString(int i)
			{ return (string)this[i]; }

		public object GetValue(int i)
			{ return this[i]; }

		public int GetValues(object[] values)
			{
			for (int index = 0; index < this.FieldCount; index++)
				values[index] = this[index];

			return this.FieldCount;
			}

		public bool IsDBNull(int i)
			{ return this[i] == null; }

		#endregion
		}
	}
